package com.example.drawingapp;

import java.io.File;
import java.io.FileOutputStream;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.EmbossMaskFilter;
import android.graphics.MaskFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

public class DrawingActivity extends Activity implements ColorPickerDialog.OnColorChangedListener {    

	// Declarations
	private Paint       mPaint; // The paint brush
	private MaskFilter  mEmboss; // Embossing filter
	private MaskFilter  mBlur; // Bluring filter
	private DrawView	mDraw; // View that holds the drawing canvas
	private MediaScannerConnection mediaConn = new MediaScannerConnection(DrawingActivity.this, null); // Needed to update the Photo Gallery android uses.
	
	// Called when the activity is created. This is when the program is launched.
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mDraw = new DrawView(this); // Assign the DrawView for retrieving later.
		setContentView(mDraw);
		
		mPaint = new Paint(); // Initialize and set up the paint brush we will use.
		mPaint.setAntiAlias(true);
		mPaint.setDither(true);
		mPaint.setColor(0xFFFF0000); // Set the default color
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setStrokeJoin(Paint.Join.ROUND);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mPaint.setStrokeWidth(12);

		mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
		mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
		
		mediaConn.connect(); // MediaScanner needs to be connected before it can begin scanning.
	}
	
	// Called when the activity is destroyed. This is when the application is closed. 
	@Override
	protected void onDestroy() {
		mediaConn.disconnect(); // Free mediaConnection.
	}

	// This is the interface listed by ColorPickerDialog class. This is called when the color is picked in that dialog.
	public void colorChanged(int color) {
		mPaint.setColor(color);
	}

	private static final int SAVE_MENU_ID = Menu.FIRST;
	private static final int COLOR_MENU_ID = Menu.FIRST + 1;
	private static final int EMBOSS_MENU_ID = Menu.FIRST + 2;
	private static final int BLUR_MENU_ID = Menu.FIRST + 3;
	private static final int ERASE_MENU_ID = Menu.FIRST + 4;
	private static final int SRCATOP_MENU_ID = Menu.FIRST + 5;
	
	// Called when the phone's menu button is pressed.
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);

		menu.add(0, SAVE_MENU_ID, 0, "Save"); // Pragmatically add the menu items.
		menu.add(0, COLOR_MENU_ID, 0, "Color").setShortcut('3', 'c');
		menu.add(0, EMBOSS_MENU_ID, 0, "Emboss").setShortcut('4', 's');
		menu.add(0, BLUR_MENU_ID, 0, "Blur").setShortcut('5', 'z');
		menu.add(0, ERASE_MENU_ID, 0, "Erase").setShortcut('5', 'z');
		menu.add(0, SRCATOP_MENU_ID, 0, "SrcATop").setShortcut('5', 'z');

		return true;
	}

	// Called right before the Menu is displayed. This must return true to display the menu.
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);
		return true;
	}

	// Called when an option has been selected from the Menu.
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		mPaint.setXfermode(null);
		mPaint.setAlpha(0xFF);

		switch (item.getItemId()) {
		
			case SAVE_MENU_ID:
				String storageDir = Environment.getExternalStorageDirectory() + File.separator + "Drawings";
				String fileName = Long.toString(System.currentTimeMillis());
				String finalPath = storageDir + File.separator + fileName + ".jpg";
				
				try {
					File f = new File(storageDir);
					f.mkdirs();
					f = new File(finalPath);
					f.createNewFile();
					FileOutputStream fos = new FileOutputStream(f);
					mDraw.getBitmap().compress(Bitmap.CompressFormat.JPEG, 100, fos);
					fos.flush();
					fos.close();
					mediaConn.scanFile(finalPath, "image/jpeg"); // We tell the media connection to scan this specific file to prevent the phone from rescanning all files on the SD card.
					Toast.makeText(getApplicationContext(), "Successfully saved to " + finalPath, Toast.LENGTH_SHORT).show(); // Generic success message.
				} catch(Exception ex) {
					Toast.makeText(getApplicationContext(), ex.toString(), Toast.LENGTH_LONG).show();
				}
				break;
				
			case COLOR_MENU_ID:
				new ColorPickerDialog(this, this, mPaint.getColor()).show();
				return true;
			
			case EMBOSS_MENU_ID:
				if (mPaint.getMaskFilter() != mEmboss) {
					mPaint.setMaskFilter(mEmboss);
				} else {
					mPaint.setMaskFilter(null);
				}
				return true;
			
			case BLUR_MENU_ID:
				if (mPaint.getMaskFilter() != mBlur) {
					mPaint.setMaskFilter(mBlur);
				} else {
					mPaint.setMaskFilter(null);
				}
				return true;
			
			case ERASE_MENU_ID:
				mPaint.setXfermode(new PorterDuffXfermode(
						PorterDuff.Mode.CLEAR));
				return true;
			
			case SRCATOP_MENU_ID:
				mPaint.setXfermode(new PorterDuffXfermode(
						PorterDuff.Mode.SRC_ATOP));
				mPaint.setAlpha(0x80);
				return true;
		}
		
		return super.onOptionsItemSelected(item);
	}
	
	
	// Sub class that will be our main content view for this application.
	// This will deal with the actual drawing container.
	public class DrawView extends View {

		private Bitmap  mBitmap;
		private Canvas  mCanvas;
		private Path    mPath; // This stores the pathing data. We will use this to handle our tracings in the Touch listener.
		private Paint   mBitmapPaint;

		public DrawView(Context c) {
			super(c);
			this.setBackgroundColor(android.R.color.white);
			DisplayMetrics metrics = new DisplayMetrics(); // DisplayMetrics is used to resolve the  physical window size for drawing.
			getWindowManager().getDefaultDisplay().getMetrics(metrics); // This initializes the metrics manager object.
			mBitmap = Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels, Bitmap.Config.ARGB_8888); // Now we use the metric sizings for the Bitmap.
			mCanvas = new Canvas(mBitmap);
			mPath = new Path();
			mBitmapPaint = new Paint(Paint.DITHER_FLAG);
		}
		
		// Return the bitmap image so we can save it.
		private Bitmap getBitmap() {
			return mBitmap;
		}

		@Override
		protected void onDraw(Canvas canvas) {
			canvas.drawColor(0xFFFFFFFF); // This will make our canvas white.

			canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

			canvas.drawPath(mPath, mPaint);
		}

		private float mX, mY;
		private static final float TOUCH_TOLERANCE = 4; // This tolerance value is used so the touchscreen isn't updated with every pixel. 
		// This is necessary since the touchscreen picks up every minute change in resistance.  

		private void touch_start(float x, float y) {
			mPath.reset();
			mPath.moveTo(x, y);
			mX = x;
			mY = y;
		}
		private void touch_move(float x, float y) {
			float dx = Math.abs(x - mX); // Here we will calculate the difference of pixel movement.
			float dy = Math.abs(y - mY);
			if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { // If the movement is above the tolerance in any direction, update the pathing.
				mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
				mX = x;
				mY = y;
			}
		}
		private void touch_up() {
			mPath.lineTo(mX, mY);
			// commit the path to our offscreen
			mCanvas.drawPath(mPath, mPaint);
			// kill this so we don't double draw
			mPath.reset();
		}

		// This is called whenever the touch screen receives movement.
		@Override
		public boolean onTouchEvent(MotionEvent event) {
			float x = event.getX();
			float y = event.getY();

			switch (event.getAction()) { // when finger is placed on View
				case MotionEvent.ACTION_DOWN:
					touch_start(x, y);
					invalidate(); // Used to signal onDraw method. This will ensure our path gets updated live.
					break;
				case MotionEvent.ACTION_MOVE: // when finger moves on View
					touch_move(x, y);
					invalidate();
					break;
				case MotionEvent.ACTION_UP: // when finger is lifted from View 
					touch_up();
					invalidate();
					break;
			}
			return true;
		}
	}
}
